﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Runtime.CompilerServices;
using System.Diagnostics;
using System.Reflection;

namespace llAma
{
    class DEObject
    {
        Delegate method;
        object[] param;
        public DEObject(Delegate d)
            : this(d, null)
        {
        }
        public DEObject(Delegate d, params object[] args)
        {
            param = args;
            method = d;
        }
        public object Execute()
        {
            return method.DynamicInvoke(param);
        }
        public MethodInfo Method
        {
            get { return method.Method; }
        }
    }
    public class DEPool
    {
        List<DelayedExecution> threads = new List<DelayedExecution>();
        int mCounter = 0;
        int mMax = 8;
        public DEPool()
        {
            //Do nothing, everything is already set.
        }
        public DEPool(int max)
        {
            mMax = max;
        }
        [MethodImpl(MethodImplOptions.Synchronized)]
        public DelayedExecution GetPump()
        {
            mCounter++;
            mCounter = mCounter % mMax;
            while (threads.Count < mCounter + 1)
            {
                threads.Add(new DelayedExecution("default"));
            }
            Debug.WriteLine(string.Format("Thread #{0} has been assigned from a private pool of {1} threads.", mCounter + 1, mMax));
            return threads[mCounter];
        }
    }
    public class DelayedExecution
    {
        static List<DelayedExecution> threads = new List<DelayedExecution>();
        static int counter = 0;
        static int max = 4;
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static DelayedExecution GetPump()
        {
            counter++;
            counter = counter % max;
            while (threads.Count < counter + 1)
            {
                threads.Add(new DelayedExecution(string.Format("default-{0}", counter)));
            }
            Debug.WriteLine(string.Format("Thread #{0} has been assigned the public pool of {1} threads.", counter, max));
            return threads[counter];
        }
        Thread myExecThread;
        //AutoResetEvent signal = new AutoResetEvent(false);
        ManualResetEvent signal = new ManualResetEvent(true);
        Queue<DEObject> execQueue = new Queue<DEObject>();
        private bool mEnd = false;
        public void Stop()
        {
            myExecThread.Abort();
        }
        public void Start()
        {
            myExecThread.Start();
        }
        public DelayedExecution(string name)
        {
            myExecThread = new Thread(ExecutionLoop);
            myExecThread.Start();
        }
        public void Invoke(Delegate d)
        {
            Invoke(d, new object[0]);
        }
        public bool InvokeRequired
        {
            get
            {
                return (Thread.CurrentThread.ManagedThreadId != myExecThread.ManagedThreadId);
            }
        }
        public void Invoke(Delegate d, params object[] args)
        {
            lock (execQueue)
            {
                if (d.Method.GetParameters().Length != args.Length)
                    throw new ArgumentException("Incorrect number of parameters specified!");
                execQueue.Enqueue(new DEObject(d, args));
                signal.Set();
            }
        }
        private void ExecutionLoop(object state)
        {
            try
            {
                while (!mEnd)
                {
                    try
                    {
                        lock (execQueue)
                        {
                            if (execQueue.Count == 0)
                                signal.Reset(); // Reset to an unsignaled state (which will cause WaitOne to block)
                            //We do this in a lock, so we don't reset it after its been set because something was added to the queue.
                        }
                        //By doing signaling, We can dynamically run data on different threads, while keeping CPU usage low.
                        //We never wake up when we don't have to.
                        signal.WaitOne(-1);
                        DEObject o = null;
                        lock (execQueue) //Keep the queue synchronized.
                        {
                            if (execQueue.Count > 0)
                            {
                                o = execQueue.Dequeue(); //Pull an item out of the queue
                            }
                        }
                        if (o != null) //If we got an item, Execute it.
                        {
                            DateTime mStart = DateTime.Now;
                            o.Execute();
                            TimeSpan runTime = DateTime.Now - mStart;
                            if(runTime.TotalMilliseconds != 0)
                                Debug.WriteLine(string.Format("Executed method total run time of method '{0}' was: {1}", o.Method.Name, runTime.ToString()));
                        }

                    }
                    catch (Exception e)
                    {
                        if (e is ThreadAbortException)
                            throw e; //Rethrow it, so the thread exits.
                    }
                }
            }
            catch (ThreadAbortException)
            {
                //Time to leave.. :lol:
                return;
            }
        }
    }
}
